package com.navinfo.platform.trip.common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.Collection;

import static java.math.BigDecimal.ROUND_HALF_UP;

/**
 * BigDecimal工具类.
 * 注意double转换 BigDecimal时需要先转换成string，防止精度混乱.
 *
 * @author 水哥
 */
public class BigDecimalUtils {
    private static final Logger logger = LoggerFactory.getLogger(BigDecimalUtils.class);
    /**
     * 乘法int*int.
     *
     * @param a int类型数字
     * @param b int类型数字
     * @return BigDecimal
     */
    public static BigDecimal multiply(Integer a, Integer b) {
        return BigDecimal.valueOf(a).multiply(BigDecimal.valueOf(b));
    }

    /**
     * 乘法double*int，四舍五入.
     *
     * @param a     double类型数字
     * @param b     int类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal multiply(Double a, Integer b, int scale) {
        return BigDecimal.valueOf(a).multiply(BigDecimal.valueOf(b)).setScale(scale, ROUND_HALF_UP);
    }

    /**
     * 乘法double*double，四舍五入.
     *
     * @param a     double类型数字
     * @param b     double类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal multiply(Double a, Double b, int scale) {
        return BigDecimal.valueOf(a).multiply(BigDecimal.valueOf(b)).setScale(scale, ROUND_HALF_UP);
    }

    /**
     * 乘法BigDecimal*int，四舍五入.
     *
     * @param a     BigDecimal类型数字
     * @param b     int类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal multiply(BigDecimal a, Integer b, int scale) {
        return a.multiply(BigDecimal.valueOf(b)).setScale(scale, ROUND_HALF_UP);
    }

    /**
     * 除法long/int，四舍五入.
     * @param a     Long类型数字
     * @param b     int类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal divide(Long a, Integer b, int scale) {
        if(null==a || 0==a || null==b || 0==b){
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), scale, ROUND_HALF_UP);
    }

    /**
     * 除法Double/BigDecimal，四舍五入.
     *
     * @param a     Double类型数字
     * @param b     BigDecimal类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal divide(Double a, BigDecimal b, int scale) {
        if(null==a || 0.0==a || null==b || BigDecimal.ZERO.equals(b)){
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(a).divide(b, scale, ROUND_HALF_UP);
    }

    /**
     * 除法Double/BigDecimal，四舍五入.
     *
     * @param a     Double类型数字
     * @param b     Double类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal divide(Double a, Double b, int scale) {
        if(null==a || 0.0==a || null==b || 0.0==b){
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), scale, ROUND_HALF_UP);
    }
    /**
     * 除法Integer/Integer，四舍五入.
     *
     * @param a     Integer类型数字
     * @param b     Integer类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal divide(Integer a, Integer b, int scale) {
        if(null==a || 0==a || null==b || 0==b){
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), scale, ROUND_HALF_UP);
    }

    /**
     * 除法Integer/Long，四舍五入.
     *
     * @param a     Integer类型数字
     * @param b     Long类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal divide(Integer a, Long b, int scale) {
        if(null==a || 0==a || null==b || 0==b){
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), scale, ROUND_HALF_UP);
    }

    /**
     * 除法Double/Long，四舍五入.
     *
     * @param a     Double类型数字
     * @param b     Long类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal divide(Double a, Long b, int scale) {
        if(null==a || 0.0==a || null==b || 0==b){
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), scale, ROUND_HALF_UP);
    }

    /**
     * 除法Double/Integer，四舍五入.
     *
     * @param a     Double类型数字
     * @param b     Integer类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal divide(Double a, Integer b, int scale) {
        if(null==a || 0.0==a || null==b || 0==b){
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), scale, ROUND_HALF_UP);
    }

    /**
     * 除法BigDecimal/Integer，四舍五入.
     *
     * @param a     BigDecimal类型数字
     * @param b     Integer类型数字
     * @param scale 保留小数点位数精度
     * @return BigDecimal
     */
    public static BigDecimal divide(BigDecimal a, Integer b, int scale) {
        if(null==a || BigDecimal.ZERO.equals(a) || null==b || 0==b){
            return BigDecimal.ZERO;
        }
        return a.divide(BigDecimal.valueOf(b), scale, ROUND_HALF_UP);
    }

    /**
     * 计算标准差.
     * @param collection double集合
     * @return double 标准差
     */
    public static double standardDeviation(Collection<Double> collection) {
        if (null==collection || collection.size()==0) {
            throw new IllegalArgumentException("标准差算法参数错误 collection can not be empty!");
        }
        double sum = 0;
        double avg = getAverage(collection);
        for (Double i : collection) {
            sum += (i - avg) * (i - avg);
        }
        return Math.sqrt(divide(sum, collection.size(), 2).doubleValue());
    }

    /**
     * 计算平均值
     * @param collection 待计算集合
     * @return  平均值
     */
    public static double getAverage(Collection<Double> collection) {
        if (null==collection || collection.size()==0) {
            throw new IllegalArgumentException("平均值算法参数错误 collection can not be empty!");
        }
        int num = collection.size();
        double sum = 0;
        for (Double i : collection) {
            sum += i;
        }
        return divide(sum, num, 2).doubleValue();
    }

}
